﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using WimaxSimulator.Properties;

namespace WimaxSimulator.Wimax
{
    [Serializable]
    public class Traffic
    {
        public int Source { get; set; }
        public int Destination { get; set; }
        public int StartTime { get; set; }
        public int EndTime { get; set; }
    }
    [Serializable]
    public class MobileStation : Station, ISubordinate
    {
        [NonSerialized]
        private BaseStation baseStation;
        public BaseStation BaseStation
        {
            get
            {
                return baseStation;
            }
            set
            {
                baseStation = value;
            }
        }


        [NonSerialized]
        private ISuperordinate accessStation;
        public ISuperordinate AccessStation { get { return accessStation; } set { accessStation = value; } }

        [NonSerialized]
        private uint homeAddress;
        public uint HomeAddress { get { return homeAddress; } set { homeAddress = value; } }

        [NonSerialized]
        private uint careOfAddress;
        public uint CareOfAddress
        {
            get
            {
                return careOfAddress;
            }
            set
            {
                careOfAddress = value;
                if (HomeAddress == 0)
                    HomeAddress = careOfAddress;
            }
        }

        public int UplinkBandwidth { get; set; }

        [NonSerialized]
        private Queue<Packet> uplinkQueue = new Queue<Packet>();
        public Queue<Packet> UplinkQueue
        {
            get
            {
                if (uplinkQueue == null)
                    uplinkQueue = new Queue<Packet>();
                return uplinkQueue;
            }
        }


        public override uint Ip
        {
            get { return CareOfAddress; }
        }

        public MobileStation(int id, Point location, int coverageRadius)
            : base(id, location, coverageRadius)
        {
            CareOfAddress = 0x00000000;
        }

        public override void Receive(Packet p)
        {
            SimulationEngine.ReceivedData[Id] += p.Size;
            updateTrafficAnalysis(false, p.Source.Ip, p.Size);
            TotalULReceived += p.Size;
        }

        public void addTraffic(int destination, int startTime, int endTime)
        {
            TrafficDestinations.Add(new Traffic() { Destination = destination, StartTime = startTime, EndTime = endTime });
        }

        /// <summary>
        /// Sends to all destinations equal amount of data packet
        /// Fully utilizes the bandwidth dedicated to this station
        /// </summary>
        public override void Send()
        {
            int count = (from t in TrafficDestinations where t.StartTime <= SimulationEngine.SimulationTime && t.EndTime >= SimulationEngine.SimulationTime select t).Count();
            if (AccessStation == null || count == 0)
                return;
            int bw = Settings.Default.MsTrafficBandwidth / count;
            foreach (Traffic dest in TrafficDestinations)
            {
                if (dest.StartTime > SimulationEngine.SimulationTime || dest.EndTime < SimulationEngine.SimulationTime)
                    continue;
                uint add = SimulationEngine.GetAddressById(dest.Destination);
                if (add > 0)
                {
                    Station access = (Station)AccessStation;
                    int dataSize = bw * (int)this.getMCS(access.Location);
                    access.Receive(new Packet(this, SimulationEngine.GetStationById(dest.Destination), dataSize) { LastStation = this });
                    TotalULSent += dataSize;
                    updateTrafficAnalysis(true, add, dataSize);
                }
            }
        }

        public void PathSelect(List<ISuperordinate> parents)
        {
            if (parents.Count == 0)
                return;
            foreach (ISuperordinate parent in parents)
            {
                if (!SentTrafficAnalysis.ContainsKey(parent))
                    SentTrafficAnalysis.Add(parent, 1);
                if (!ReceivedTrafficAnalysis.ContainsKey(parent))
                    ReceivedTrafficAnalysis.Add(parent, 1);
            }
            ISuperordinate access = (from p in parents
                                     where rate(p) == (from r in parents
                                                       select rate(r)).Max()
                                     select p).FirstOrDefault<ISuperordinate>();


            if (AccessStation != access)
            {
                SimulationEngine.Log(Id, "Candidate access station" + ((Station)access ).Id+"\tavaliable bandwidth"+access.AvaliableBandwidth+ "\tCurrent AccessStation" + AccessStation);

                if (!connectToStation(access))
                {
                    SimulationEngine.Log(Id, "Cannot connect to " + ((Station)access).Id);
                    parents.Remove(access);
                    if (parents.Count > 0)
                        PathSelect(parents);
                    else
                    {
                        SimulationEngine.Log(Id, "There is no avaliable stations left Current AccessStation\t" + AccessStation);
                        
                    }
                }

            }
        }
        public void ForceDisconnect()
        {
            AccessStation.Disconnect(this);
            CareOfAddress = 0;
            BaseStation = null;
            AccessStation = null;
        }
        private bool connectToStation(ISuperordinate accessStation)
        {
            uint ip;
            if (accessStation != null && (ip = accessStation.Connect(this)) > 0)
            {
                if (AccessStation != null)
                    AccessStation.Disconnect(this);
                this.AccessStation = accessStation;
                CareOfAddress = ip;
                return true;
            }
            return false;
        }

        internal void RemoveTraffic(Traffic t)
        {
            Traffic tr = (from tra in TrafficDestinations
                          where tra.Destination == t.Destination && tra.StartTime == t.StartTime && tra.EndTime == t.EndTime
                          select tra).Single<Traffic>();
            TrafficDestinations.Remove(tr);
        }

        public override string ToString()
        {
            return IpToStr(CareOfAddress);
        }
    }
}
